package com.platform.common.log;

import com.platform.common.utils.DateUtils;
import com.platform.common.utils.ParamUtils;
import com.platform.common.utils.UserUtils;
import com.platform.upms.model.Log;
import com.platform.upms.service.LogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

@Aspect
@Component
public class SystemLogAspect {

    private Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);

    private static final ThreadLocal<Date> beginTimeThreadLocal = new NamedThreadLocal<>("ThreadLocal beginTime");

    @Autowired
    private LogService logService;

    /**
     * Controller层切点 注解拦截
     */
    @Pointcut("@annotation(com.platform.common.log.SystemLog)")
    public void logAspect(){}

    /**
     * 前置通知 用于拦截Controller层记录用户的操作的开始时间
     * @param joinPoint 切点
     * @throws InterruptedException
     */
    @Before("logAspect()")
    public void doBefore(JoinPoint joinPoint) throws InterruptedException{
        HttpServletRequest request = getRequest();
        Date beginTime=new Date();
        beginTimeThreadLocal.set(beginTime);//线程绑定变量（该数据只有当前请求的线程可见）
        if (logger.isDebugEnabled()){//这里日志级别为debug
            logger.debug("开始计时: {}  URI: {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(beginTime), request.getRequestURI());
        }
    }

    /**
     * 后置通知 用于拦截Controller层记录用户的操作
     * @param joinPoint 切点
     */
    @After("logAspect()")
    public void doAfter(JoinPoint joinPoint) {
        HttpServletRequest request = getRequest();
        long beginTime = beginTimeThreadLocal.get().getTime();//得到线程绑定的局部变量（开始时间）
        long endTime = System.currentTimeMillis();  //2、结束时间
        String timeout = DateUtils.formatDateTime(endTime - beginTime);
        if (logger.isDebugEnabled()){
            logger.debug("计时结束：{}  URI: {}  耗时： {}   最大内存: {}m  已分配内存: {}m  已分配内存中的剩余空间: {}m  最大可用内存: {}m", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(endTime), request.getRequestURI(), timeout, Runtime.getRuntime().maxMemory()/1024/1024, Runtime.getRuntime().totalMemory()/1024/1024, Runtime.getRuntime().freeMemory()/1024/1024, (Runtime.getRuntime().maxMemory()-Runtime.getRuntime().totalMemory()+Runtime.getRuntime().freeMemory())/1024/1024);
        }

        try {
            Log log = new Log();
            log.setType("0");
            log.setContent(getDescription(joinPoint));
            log.setCreateTime(beginTimeThreadLocal.get());
            log.setTimeOut(timeout);
            log.setIpAddress(request.getRemoteAddr());
            log.setMacAddress("");
            log.setVisitMethod(request.getRequestURI());
            log.setVisitParam(ParamUtils.getParamToString(request));
            log.setUserId(UserUtils.getUserId());
            logService.saveData(log);
        } catch (Exception e) {
            logger.error("日志保存失败");
        }
    }

    /**
     *  异常通知 记录操作报错日志
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "logAspect()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        HttpServletRequest request = getRequest();
        long beginTime = beginTimeThreadLocal.get().getTime();//得到线程绑定的局部变量（开始时间）
        long endTime = System.currentTimeMillis();  //2、结束时间
        String timeout = DateUtils.formatDateTime(endTime - beginTime);
        try {
            Log log = new Log();
            log.setType("1");
            log.setContent(e.toString());
            log.setCreateTime(beginTimeThreadLocal.get());
            log.setTimeOut(timeout);
            log.setIpAddress(request.getRemoteAddr());
            log.setMacAddress("");
            log.setVisitMethod(request.getRequestURI());
            log.setVisitParam(null);
            log.setUserId(UserUtils.getUserId());
            logService.saveData(log);
        } catch (Exception ex) {
            logger.error("日志保存失败");
        }
    }

    public static HttpServletRequest getRequest() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return request;
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception 获取注解失败
     */
    private static String getDescription(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method m = signature.getMethod();
        return m.getAnnotation(SystemLog.class).description();
    }
}
